'use strict'

//切换到当前目录
process.chdir(__dirname)

const titbit = require('titbit')
const fs = require('fs')
const dbcfg = require('./dbconfig.js')
const pg = require('pg')
const wxkey = require('./wxkey.js')
const { httpcli } = require('gohttp')
const token = require('titbit-token')
const tbloader=require('titbit-loader')

// 解决跨域
// const {cors} = require('titbit-toolkit')

// 通过try-catch保证images的存在，如果存在则不执行其他操作，如果不存在则再创建一个images目录
try {
    fs.accessSync('./images')
} catch {
    fs.mkdirSync('./images')
}

// 创建一个连接数据库的连接池
let psql = new pg.Pool(dbcfg)


let cert_path = '/usr/local/share'


const app = new titbit({
    //开启调式模式，若有错误会输出错误信息
    debug: true,
    globalLog: true,//开启全局日志
    cert: `${cert_path}/api.lzjlw.top.pem`,
    key: `${cert_path}/api.lzjlw.top.key`
})


let tb=new tbloader()
tb.init(app)

// 依赖注入：把想要的全局变量写在app.addService里，可以全局引用，可以通过c.service获取（c:全局上下文）
app.addService('psql',psql)
app.addService('imagePath',`${__dirname}/images`)

// 解决跨域
// app.pre(new cors)

app.get('/', async c => {
    c.send('ok')
})


/**---------------content路由------------------ */

//获取内容列表
// app.get('/content', async c => {
    // let ret = await psql.query(`select * from content`)
    // //c.send(ret.rows)等效于c.res.body=ret.rows
    // c.send(ret.rows)
// })

//获取具体内容
// app.get('/content/:id', async c => {
//     let sqltext = `SELECT * FROM content WHERE id=$1`

//     let r = await psql.query(sqltext, [c.param.id])

//     if (r.rowCount === 0) {
//         return c.status(404).send('content not found')
//     }
//     // 返回点击的列表信息
//     c.send(r.rows[0])
// })

//创建内容
// app.post('/content', async c => {
//     try {
//         //提交的body数据通过c.body获取
//         let data = JSON.parse(c.body)

//         let sqltext = `INSERT INTO content(id,add_time,detail,title,update_time) `
//             + ` VALUES ($1,$2,$3,$4,$5)`

//         // 生成唯一ID
//         let id = c.helper.makeId()//助手函数
//         let tm = Date.now()

//         let r = await psql.query(sqltext, [id, tm, data.detail, data.title, tm])

//         //没有成功向数据表里插入数据
//         if (r.rowCount === 0) {
//             return c.status(500).send('failed')
//         }

//         // 如果成功插入，返回唯一ID值
//         c.send(id)
//     } catch (err) {
//         c.status(400).send('it is not json data')
//     }
// })

// app.delete('/content/:id', async c => {
//     let sqltext = `DELETE  FROM content WHERE id=$1`

//     let r = await psql.query(sqltext, [c.param.id])

//     if (r.rowCount == 0) {
//         return c.status(404).send('content not found')
//     }

//     c.send(r)
// })


/**---------------content路由   -END  ------------------ */

app.get('/upload', async c => {
    //c.reply -> response in HTTP/1.1
    //c.reply -> stream in HTTP/2
    //c.reply 是响应对象，可写的流，兼容HTTP1和HTTP2
    //在HTTP/1.1中指向response，在HTTP/2中指向http2stream
    await c.helper.pipe('./upload.html', c.reply)//相当于将'./upload.html'赋给c.reply这个可写流
    console.log('get')
})



// app.post('/upload', async c => {
    //根据name获取文件对象
    // let f = c.getFile('image')

    // if (!f) {
    //     return c.status(400).send('image not found')
    // }

    // //根据传入的原始文件生成唯一文件名，主要使用f.filename的扩展名
    // let fname = c.helper.makeName(f.filename)

    // //移动文件到指定位置
    // await c.moveFile(f, `./images/${fname}`)

    // c.send(fname)
// })

// app.get('/static/*',async c=>{//引入静态资源，*代表任意路径，后面可以写任意深度的路径

// })

//返回图片数据
// app.get('/image/:name', async c => {//冒号后面的参数为动态变化的   ./image/a.jpg
    //c.param是一个对象，存放解析到的数据，其中的key值是路由参数的名称
    // let imgname = c.param.name

    // await c.helper.pipe(`./images/${imgname}`, c.reply)


// })

let tok = new token({
    // token解密验证3600秒即为超时（token有效期，单位为秒）
    expires: 3600,
    // 必须是32位字符
    key: 'ashdisjow82910wjsidhaolp9748wj1s',//加密字符

    // 必须是16位字符
    iv: '18dhsjaosjwosnvj'//干扰项
})

app.addService('tok',tok)

// 向微信服务器请求
app.get("/mp-login/:code", async c => {
    // wxkey.appid小程序id
    // wxkey.secret小程序秘钥
    let auth_url = `https://api.weixin.qq.com/sns/jscode2session?appid=${wxkey.appid}&secret=${wxkey.secret}&js_code=${c.param.code}&grant_type=authorization_code`

    let ret = await httpcli.get(auth_url)
        .then(res => {
            return res.json()
        })
        
    // 返回一个加密的结果，结果中包含openid（用户唯一标识）、session_key（会话密钥：是对用户数据进行加密签名的密钥）
    // 解密后的结果
    // {"ok":true,"data":{"session_key":"x0pBMwkqKhqg9cBv9iT0aw==","openid":"oIbNU5SkBUdLDJ8xyg7lBfltdCSE",
    // "expires":45000,"timestamp":1633952878995,"tokenId":""},"now":1633952891421}
    c.send(tok.make(ret))
})

// 验证加密（https://api.lzjlw.top:4569/decrypt/token）如果成功则返回解密后的值，如果失败则返回{"ok":false,"errcode":"TIMEOUT"}
app.get("/decrypt/:token", async c => {
    c.send(tok.verify(c.param.token))
})

//中间件（middleware）作用是拦截不想发生的情况
// app.pre(async (c, next) => {
    // authorization：请求消息头含有服务器用于验证用户代理身份的凭证
//     let token = c.headers.authorization || c.query.token
//     if (!token) {
//         return c.status(403).send('deny')
//     }

//     let r = tok.verify(token)

//     if (r.ok === false) {
//         return c.status(403).send(r.errcode)
//     }

//     //c.box就是一个空对象，作用是放东西。
//     c.box.user = r.data

//     await next()

// })

//运行在1234端口
app.run(4569)


// 本地代码  --push-->  gitee  --pull-->  云服务 
// 浏览器  通过 HTTP 运行run


/*
    HTTP消息头content-type指定了数据的类型（数据的格式）
    Client                                       Server
       content-type描述的值                          根据content-type决定如何解析Body数据
       要和提交的body数据一致

       content-type：
        multipart/form-data;boundary=xxx ----------- 文件解析

        text/*                           ----------- 文本类型

        application/*                    ----------- 要看具体值，application/json也是文本类型

        ......                           ----------- 每种类型如何处理是双方设计好的通信方式

        到web框架这一层面，则如何处理是开发框架设计的，但通常都要遵循一个原则：
                允许开发者做自己的处理

        在本框架范围内：
                multipart类型  c.body是object类型，文件信息解析到c.files
                application/x-www-form-urlencoded传统的表单类型，c.body是object类型，用于获取提交的表单数据
                text/* c.body是文本类型，c.body=c.raw.toString('utf8')。
                其他类型： c.body 是buffer类型，就是对c.rawBody的引用
*/